package com.hk.core.authentication.api.reactive;

import com.hk.commons.util.ArrayUtils;
import com.hk.core.authentication.api.UserPrincipal;
import reactor.core.publisher.Mono;

import java.util.Arrays;

/**
 * @author Kevin
 * @date 2022-06-24 20:44
 */
public interface ReactiveSecurityContext {

    /**
     * 获取当前登陆的用户
     *
     * @return {@link UserPrincipal}
     */
    default Mono<UserPrincipal> getPrincipal() {
        return getPrincipalByType(UserPrincipal.class);
    }

    /**
     * 获取当前登陆的用户返回指定的类型
     */
    <T extends UserPrincipal> Mono<T> getPrincipalByType(Class<T> type);

    /**
     * 当前用户是否有认证
     *
     * @return 认证返回true, 否则返回false
     */
    Mono<Boolean> isAuthenticated();

    /**
     * 判断当前登陆用户是否有任意指定权限
     */
    default Mono<Boolean> hasAnyPermission(String... permissions) {
        if (ArrayUtils.isEmpty(permissions)) {
            return Mono.just(false);
        }
        return isAuthenticated()
                .defaultIfEmpty(false)
                .flatMap(auth -> auth ? getPrincipal()
                        .map(principal -> Arrays.stream(permissions)
                                .anyMatch(principal::hasPermission))
                        .defaultIfEmpty(false) : Mono.just(false));
    }

    /**
     * 判断当前登陆用户是否有指定权限
     */
    default Mono<Boolean> hasPermission(String permission) {
        return isAuthenticated()
                .defaultIfEmpty(false)
                .flatMap(auth -> auth ? getPrincipal()
                        .map(principal -> principal.hasPermission(permission))
                        .defaultIfEmpty(false) : Mono.just(false));
    }

    /**
     * 判断当前登陆用户是否有指定角色
     */
    default Mono<Boolean> hasRole(String role) {
        return isAuthenticated()
                .defaultIfEmpty(false)
                .flatMap(auth -> auth ? getPrincipal()
                        .map(principal -> principal.hasRole(role))
                        .defaultIfEmpty(false) : Mono.just(false));
    }

    /**
     * 判断当前登陆用户是否有任意指定角色
     */
    default Mono<Boolean> hasAnyRole(String... roles) {
        if (ArrayUtils.isEmpty(roles)) {
            return Mono.just(false);
        }
        return isAuthenticated()
                .defaultIfEmpty(false)
                .flatMap(auth -> auth ? getPrincipal()
                        .map(principal -> Arrays.stream(roles)
                                .anyMatch(principal::hasRole))
                        .defaultIfEmpty(false) : Mono.just(false));
    }

}
